/*
 * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.pig4cloud.pig.auth.support.handler;

import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pig.admin.api.entity.SysLog;
import com.pig4cloud.pig.common.core.constant.CommonConstants;
import com.pig4cloud.pig.common.core.util.SpringContextHolder;
import com.pig4cloud.pig.common.core.util.WebUtils;
import com.pig4cloud.pig.common.log.event.SysLogEvent;
import com.pig4cloud.pig.common.log.util.SysLogUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.event.LogoutSuccessEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.stereotype.Component;

/**
 * 用户退出成功事件处理器
 * <p>
 * 该类实现了 Spring 的 ApplicationListener 接口，
 * 监听和处理 LogoutSuccessEvent 事件。
 * 
 * 主要功能：
 * 1. 监听用户退出成功事件
 * 2. 记录退出日志（包括用户名、令牌、耗时等）
 * 3. 发布异步日志事件到系统日志服务
 * 
 * 支持的认证类型：
 * - PreAuthenticatedAuthenticationToken：预认证令牌
 *
 * @author lengleng
 * @date 2025/05/30
 */
@Slf4j
@Component
public class PigLogoutSuccessEventHandler implements ApplicationListener<LogoutSuccessEvent> {

	/**
	 * 处理登出成功事件
	 * <p>
	 * 当用户成功退出时，Spring Security 会发布 LogoutSuccessEvent 事件。
	 * 该方法会接收并处理这个事件。
	 * 
	 * @param event 登出成功事件，包含认证信息
	 */
	@Override
	public void onApplicationEvent(LogoutSuccessEvent event) {
		// 从事件中获取认证对象
		Authentication authentication = (Authentication) event.getSource();
		// 只处理预认证令牌类型的退出
		if (authentication instanceof PreAuthenticatedAuthenticationToken) {
			handle(authentication);
		}
	}

	/**
	 * 处理退出成功方法
	 * <p>
	 * 详细处理流程：
	 * 1. 记录退出成功日志
	 * 2. 构建系统日志对象
	 * 3. 记录请求中的令牌信息（Authorization 头）
	 * 4. 计算请求耗时
	 * 5. 记录服务 ID（从认证凭据中获取）
	 * 6. 发布异步日志事件
	 * 
	 * @param authentication 登录对象，包含用户信息和凭据
	 */
	public void handle(Authentication authentication) {
		// 记录退出成功日志
		log.info("用户：{} 退出成功", authentication.getPrincipal());
		
		// 构建系统日志对象
		SysLog logVo = SysLogUtils.getSysLog();
		logVo.setTitle("退出成功");

		// 从请求中获取附加信息
		WebUtils.getRequest().ifPresent(request -> {
			// 记录 Authorization 请求头作为参数
			// 这通常包含 Bearer 令牌
			logVo.setParams(request.getHeader(HttpHeaders.AUTHORIZATION));
			
			// 计算请求耗时
			String startTimeStr = request.getHeader(CommonConstants.REQUEST_START_TIME);
			if (StrUtil.isNotBlank(startTimeStr)) {
				Long startTime = Long.parseLong(startTimeStr);
				Long endTime = System.currentTimeMillis();
				logVo.setTime(endTime - startTime);
			}
		});

		// 设置服务 ID
		// 对于预认证令牌，服务 ID 存储在 credentials 中
		if (authentication instanceof PreAuthenticatedAuthenticationToken) {
			logVo.setServiceId(authentication.getCredentials().toString());
		}
		
		// 设置日志创建者
		logVo.setCreateBy(authentication.getName());
		
		// 发布异步日志事件，由日志服务处理并持久化
		SpringContextHolder.publishEvent(new SysLogEvent(logVo));
	}

}
